package cn.heiliu.selenium.service.impl;

import cn.heiliu.selenium.domain.SeleniumProxy;
import cn.heiliu.selenium.domain.SpecialUrl;
import cn.heiliu.selenium.domain.WebDriverGetResult;
import cn.heiliu.selenium.exception.DownFileTimeOutException;
import cn.heiliu.selenium.service.IWebDriverGetService;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.NoSuchWindowException;

import java.io.File;

/**
 * @author wangyelou
 * @date 2021/6/14
 */
@Slf4j
public class FirefoxWebDriverGetServiceImpl implements IWebDriverGetService {

    private String downDir;
    private FirefoxWebDriverServiceImpl webDriver;
    private String url;
    private long timeout;
    @Setter
    private String waitXpath;
    @Setter
    private SeleniumProxy proxy;
    private File downFile;
    private long startTime = System.currentTimeMillis();


    public FirefoxWebDriverGetServiceImpl() {
    }

    public FirefoxWebDriverGetServiceImpl(FirefoxWebDriverServiceImpl webDriver, String downDir, String url, long timeout) {
        this.downDir = downDir;
        this.webDriver = webDriver;
        this.url = url;
        this.timeout = timeout;
    }

    @Override
    public void get() {
        // 重置窗口
        String finalCurrentWindow = webDriver.resetWindow();

        // 删除所有下载文件
        FileUtil.clean(downDir);

        // 设置代理
        proxySet();

        // 清空当前标签页内容
        webDriver.get(SpecialUrl.FIREFOX_BLANK);

        // 执行js， 跳转页面
        webDriver.executeScript("window.open(\"" + formatUrl(url) + "\")");

        // 切换标签
        webDriver.getWindowHandles().forEach(window -> {
            if (!window.equals(finalCurrentWindow)) {
                webDriver.switchTo().window(window);
            }
        });

        // 结果处理
        dealResult();
    }

    /**
     * 下载结果处理
     */
    private void dealResult() {
        long startCheckTime = System.currentTimeMillis();
        boolean downFlag = false;
        while (System.currentTimeMillis() - startCheckTime < timeout) {
            // 下载文件检查
            if (FileUtil.exist(downDir) && FileUtil.ls(downDir).length > 0) {
                log.info("file downloading...");
                // 检测到文件下载，则等待下载完成
                while (FileUtil.ls(downDir).length > 1 && System.currentTimeMillis() - startCheckTime < timeout) {
                    ThreadUtil.sleep(1000);
                }

                // 获取下载文件路径
                for (File file : FileUtil.ls(downDir)) {
                    downFile = file;
                    log.info("get down file: {}", downFile.getName());
                }
                downFlag = true;
                break;
            }


            // html页面元素检查
            if (!StrUtil.hasEmpty(waitXpath)) {
                if (!webDriver.findElements(By.xpath(waitXpath)).isEmpty()) {
                    log.info("find element with xpath: {}", waitXpath);
                    break;
                }
            } else {
                // 如果没有设置wait xpath 则检查完是否有下载文件后就退出
                break;
            }

            ThreadUtil.sleep(500);
        }

        if (System.currentTimeMillis() - startCheckTime > timeout) {
            if (downFlag) {
                throw new DownFileTimeOutException("web driver download file time out " + timeout);
            } else {
                throw new NoSuchElementException("no such element " + waitXpath);
            }
        }


    }

    /**
     * 获取结果
     * @return
     */
    @Override
    public WebDriverGetResult getResult() {
        return new WebDriverGetResult(
                FileUtil.isFile(downFile) ? FileUtil.readBytes(downFile) : webDriver.getPageSource().getBytes(),
                downFile != null ? downFile.getAbsolutePath() : null,
                downFile != null,
                System.currentTimeMillis() - startTime
        );
    }

    /**
     * 利用js设置代理
     */
    private void proxySet() {
        StringBuilder js = new StringBuilder();
        js.append("var prefs = Components.classes['@mozilla.org/preferences-service;1'].getService(Components.interfaces.nsIPrefBranch);");
        js.append(String.format("prefs.setIntPref('network.proxy.type', %s);", proxy != null && !proxy.isEmpty() ? 1 : 0));

        if (proxy != null && !proxy.isEmpty()) {
            js.append(String.format("prefs.setCharPref('network.proxy.http', '%s');", proxy.getHost()));
            js.append(String.format("prefs.setIntPref('network.proxy.http_port', %s);", proxy.getPort()));
            js.append(String.format("prefs.setCharPref('network.proxy.ssl', '%s');", proxy.getHost()));
            js.append(String.format("prefs.setIntPref('network.proxy.ssl_port', %s);", proxy.getPort()));
        }
        webDriver.get(SpecialUrl.FIREFOX_CONFIG);
        webDriver.executeScript(js.toString());
    }

    /**
     * 根据firefox auth 插件要求配置自定义认证信息
     * @param url
     * @return
     */
    private String formatUrl(String url) {
        if (proxy != null && proxy.isAuth()) {
            return String.format("%s~%s:%s", url, proxy.getUserName(), proxy.getPassWord());
        }
        return url;
    }

}
